package com.example.springboot.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springboot.common.Constants;
import com.example.springboot.common.Result;
import com.example.springboot.entity.Files;
import com.example.springboot.mapper.FileMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

/**
 * 文件上传相关接口
 */
@RestController
@RequestMapping("/file")
public class FileController {

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @Value("${files.upload.ip}")
    private String serverIp;

    @Resource
    private FileMapper fileMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 文件上传接口
     *
     * @return file 前端传递通过来的文件
     * @throws IOException
     */

    @PostMapping("upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {

        String originalFilename = file.getOriginalFilename(); //获取原始名称
        String type = FileUtil.extName(originalFilename);  //获取文件类型
        long size = file.getSize();   //获取文件大小

        //定义一个文件唯一的标识码
        String uuid = IdUtil.fastSimpleUUID();
        //StrUtil.DOT 是.的意思
        String fileUUID = uuid + StrUtil.DOT + type; //文件类型

        File uploadFile = new File(fileUploadPath + fileUUID);

        //父级目录是否存在，若不存在创建一个files文件目录
        File parentFile = uploadFile.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdir();
        }

        //获取文件的url
        String url;
        //上传文件的磁盘
        file.transferTo(uploadFile);

        //获取文件的md5，通过对比md5文件避免重复上传相同内容的文件
        String md5 = SecureUtil.md5(uploadFile);

        //从数据库查询，是否存在相同 的记录，getFileByMd5（封装方法）
        Files dbFiles = getFileByMd5(md5);

        //判断是否有相同图片
        if (dbFiles != null) {
            //如果存在就用数据库的url
            url = dbFiles.getUrl();
            //由于文件已存在，所以上传刚才上传的重复文件
            uploadFile.delete();
        } else {
            url = "http://" + serverIp + ":9090/file/" + fileUUID;
        }

        //存储到数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size / 1024);
        saveFile.setUrl(url);
        saveFile.setMd5(md5);

        fileMapper.insert(saveFile);

        //  第一种方法 清空redis （最简单的方式）
        flushRedis(Constants.FILES_KEY);


//             第二种方法   从redis取出数据，操作完，再设置（不用查询数据库）

//             从缓存get  redis数据
//        String jsonStr = stringRedisTemplate.opsForValue().get(Constants.FILES_KEY);

//        //     从redis缓存读取数据
//        List<Files> files1 = JSONUtil.toBean(jsonStr, new TypeReference<List<Files>>() {
//        }, true);
//        files1.add(saveFile);


        //  JSONUtil.toJsonStr(files):  把files 转成json的字符串
//        setCache(Constants.FILES_KEY,JSONUtil.toJsonStr(files1));

        //第三种方法   从数据库查出数据
//        List<Files> files = fileMapper.selectList(null);

        //  JSONUtil.toJsonStr(files):  把files 转成json的字符串
//        setCache(Constants.FILES_KEY, JSONUtil.toJsonStr(files));

        return url;
    }

    @PostMapping("uploadImg")
    public Object uploadImg(@RequestParam MultipartFile file) throws IOException {

        String originalFilename = file.getOriginalFilename(); //获取原始名称
        String type = FileUtil.extName(originalFilename);  //获取文件类型
        long size = file.getSize();   //获取文件大小

        //定义一个文件唯一的标识码
        String uuid = IdUtil.fastSimpleUUID();
        //StrUtil.DOT 是.的意思
        String fileUUID = uuid + StrUtil.DOT + type; //文件类型

        File uploadFile = new File(fileUploadPath + fileUUID);

        //父级目录是否存在，若不存在创建一个files文件目录
        File parentFile = uploadFile.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdir();
        }

        //获取文件的url
        String url;
        //上传文件的磁盘
        file.transferTo(uploadFile);

        //获取文件的md5，通过对比md5文件避免重复上传相同内容的文件
        String md5 = SecureUtil.md5(uploadFile);

        //从数据库查询，是否存在相同 的记录，getFileByMd5（封装方法）
        Files dbFiles = getFileByMd5(md5);

        //判断是否有相同图片
        if (dbFiles != null) {
            //如果存在就用数据库的url
            url = dbFiles.getUrl();
            //由于文件已存在，所以上传刚才上传的重复文件
            uploadFile.delete();
        } else {
            url = "http://" + serverIp + ":9090/file/" + fileUUID;
        }

        //存储到数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size / 1024);
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        fileMapper.insert(saveFile);

        // {
//   "errno":0,
//       "data":[
//         {
//           url: "图片地址",
//         }
//      ]
// }

        return JSONUtil.parseObj("{\"errno\": 0, \"data\": [{\"url\": \"" + url + "\"}]}");
    }


    /**
     * http://localhost:9090/file/{fileUUID}
     * 下载文件接口
     */
    @GetMapping("/{fileUUID}")
    public void download(@PathVariable String fileUUID, HttpServletResponse response) throws IOException {
        // 根据文件的唯一标识吗获取文件
        File uploadFile = new File(fileUploadPath + fileUUID);
        // 数组输出流的格式
        ServletOutputStream os = response.getOutputStream();
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileUUID, "UTF-8"));
        response.setContentType("application/octet-stream");

        //读取文件的字节流
        os.write(FileUtil.readBytes(uploadFile));
        os.flush();
        os.flush();
    }

    /**
     * 通过文件md5查询文件
     *
     * @param md5
     * @return
     */
    private Files getFileByMd5(String md5) {
        //查询文件的md5是否存在
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5", md5);
        List<Files> filesList = fileMapper.selectList(queryWrapper);
        return filesList.size() == 0 ? null : filesList.get(0);

    }

    //新增和修改
    // 开启更新时的缓存
//    @CachePut(value = "files", key = "'frontAll'")
    @PostMapping("/update")
    public Result update(@RequestBody Files files) {
        fileMapper.updateById(files);
        // 清空redis
        flushRedis(Constants.FILES_KEY);
        return Result.success();
    }

    //根据id查找出数据
    @GetMapping("/detail/{id}")
    public Result getById(@PathVariable Integer id) {

        return Result.success(fileMapper.selectById(id));
    }

    //查找出所有数据
    @GetMapping("/front/all")
    public Result frontAll() {
        //新增或者更新
        return Result.success(fileMapper.selectList(null));
    }


    //清除一条缓存，key为要清空的数据
//    @CacheEvict(value="files",key="'frontAll'")
    //删除
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        Files files = fileMapper.selectById(id);
        files.setIsDelete(true);
        fileMapper.updateById(files);
        // 清空redis
        flushRedis(Constants.FILES_KEY);

        return Result.success();
    }

    //批量删除
    @PostMapping("/del/batch")
    public Result deleteBatch(@RequestBody List<Integer> ids) {
        // select * from sys_file where id in (id,id,id...)
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);
        List<Files> files = fileMapper.selectList(queryWrapper);
        for (Files file : files) {
            file.setIsDelete(true);
            fileMapper.updateById(file);
        }
        return Result.success();
    }

    /**
     * 分页查询接口
     *
     * @param pageNum
     * @param pageSize
     * @param name
     * @return
     */

    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {

        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        //查询未删除记录
        queryWrapper.eq("is_delete", false);
        queryWrapper.orderByDesc("id");
        if (!"".equals(name)) {
            queryWrapper.like("name", name);
        }

        return Result.success(fileMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper));
    }

    // 删除 缓存
    private void flushRedis(String key) {
        stringRedisTemplate.delete(key);
    }

    // 设置缓存
    private void setCache(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

}
